# -*- coding: utf-8 -*-
from collections import OrderedDict
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5
import base64
import requests
import json

from django.conf import settings

from async import async_job
from common.channel.pay import check_channel_order
from common.order import db as order_db
from common.order.model import PAY_STATUS
from common.utils import track_logging
from common.utils.exceptions import ParamError
from common.utils.ip_address import check_valid_ip_address

_LOGGER = track_logging.getLogger(__name__)

APP_CONF = {
    '201906241633042265986': {
        'pri_key': """
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCoH19szvF/iyxW
vtyNsXCkvimmcuaKP2STev4WkHor2CAYHMpiqO1rLDP9O4miqoxzBmIl/lCNIzYV
xIFCOxwVVaPslSMcB+o0zFBX+fwWaTPGdIcUsO/DlmotNBR1IYLhVQJ/tJscI4On
5CM6taJLOmSmFaNpsaHw1hVFNDXy1YMjPab6RAphNb/wropzXfncR/0vS06nLcNg
k2YZIw+wlm8/vVuLQS1+wuqNRD4opIvWKI5lBTGIf8zLhqU6b8lsyD5ta5eod+5z
Si6OQiol8rt3ghCfySddbHDyaY91csIoU87APAUMoLsnXECvvZISPXIywU7zdJ5N
xerQCpg9AgMBAAECggEBAJAq7wJ4Z0EPEEOGwczDtyueYkr4xtcQS2folVC2Lf3B
BKyIq8h0wbsgmahy0LDr/Sp589pOvbZ51lxOOOWZmJh26u3qacQUyXLjLZZzqQdy
yVJKBLfqjoAzUPNYcJ6M0RX3dwAZ2NUFY1WqXcdvkC9gcIS3k30ENTE38993STfl
GgoQjNoDlc0AaE7hCQWW81enOV7uMBv5k+1puZfCaBNngP2t3tQmHB7Uf2PdEPr/
+91ymobaZzRb9o4iAO0sD06IzQV02nYdZ996TN0XmncbdKDTeEHy3IU3zNim25RO
PWB/oJS3ICaW1Uo9i4OlXrWLTT/rdsYX9+KDti1Y4IUCgYEA0pQy5azFxQn0+nH2
LZQqSifyGHWKbbuP5j4rbz5AmzRpzzG85PiYvNnPEwB1vkCuIeGTPSEjOrLWKkBm
jrnMtCLUUV/H4HNMg3y3LKHRdhurUIIefE6Wzw2BgplVXSBAPEM3BrqQ6NDPVwGN
YKMv0u5xXIIpme9GF3lIhZM96VcCgYEAzGLO1M5BFxtlbROTJHF5sjdXeC6/aWBe
cQ6JqMx7HZXopm04fFuir35HvnVG2GMFxlfEV2BP78Lr7quTB7AjoBWZVVgnm8WI
794Py/qIM4PtU1DI7t+bS6vII0hI+iXQhw7IpCASaPx5CvgqU8v+J5sGqoNZ69O3
FElK6BT8iosCgYEAvpY49pyHpo0bdyx7pcbq+5Dp0W9YqXZPiBCOmTFbod0vQsJ+
dzH0c0Ai5czNxDKgJ7QU4gfC7dZ8dWMyL01EU2kOhtPMgH16K6tN0gxnGqFyTPxn
gRRT8/QCWmRS2WVeHeyrueBTVRPgBJw4C9XGa/85BIQeCN+Bra8sjxCzwSMCgYB5
iUwKuXd1QSJIAXrfEedW9Ze67Ll/9QFT41wylx2i0zDcPlh3gpo0FzicZeqx4/hc
IHatqqT+Iv/fFgHBcZlNqWpbmKc89vLeCEOGfT0evRWUFJbXjPowvj6t1F8o+PGO
Tlk7V6L68R8dScMJv2UrsgD9xkvjcysMEiVL6qbdzwKBgFJu+U6UBUOHRzG9Z9IW
JBwO0iTqF2Ntjnz25k9VjLzR0qIbKrntoiX6ykYl7YKMhk/ublYSjYJkfHVWynQW
dOe0cgvKLbFxXa2ABf3zCC/fpDzZdutCKMBiffQg+pzyPRUsKxuPqkRVMK3aU3S3
SKQqs6p0nHi6zhFO4ydvXkGl""",
        'pub_key': """
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqB9fbM7xf4ssVr7cjbFw
pL4ppnLmij9kk3r+FpB6K9ggGBzKYqjtaywz/TuJoqqMcwZiJf5QjSM2FcSBQjsc
FVWj7JUjHAfqNMxQV/n8FmkzxnSHFLDvw5ZqLTQUdSGC4VUCf7SbHCODp+QjOrWi
SzpkphWjabGh8NYVRTQ18tWDIz2m+kQKYTW/8K6Kc1353Ef9L0tOpy3DYJNmGSMP
sJZvP71bi0EtfsLqjUQ+KKSL1iiOZQUxiH/My4alOm/JbMg+bWuXqHfuc0oujkIq
JfK7d4IQn8knXWxw8mmPdXLCKFPOwDwFDKC7J1xAr72SEj1yMsFO83SeTcXq0AqY
PQIDAQAB""",
        'sys_pub_key': """
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt3luIDngKRrNcYPqBt/C
wH0L2r37irT9lpB/qOFKo83FsH/D4RFqvMFVLAa/M1mX5OTJoP2nMC/rzdRGf6Ny
lKMHSzYHAF07TlPpkkRBJNRR29SZ29YX7Zl/coQBYCFK+DUgIItw6ZAQvlle09is
a+TJLJKVUcIHuJwbfooi/qxfeBPp3Q+p5szTIE87QBmViFnL5NQAHA0pFcBEZxpK
lmLK0gM2zcL4drcTmMJJ3vvual5j1xH5dtKK2QZjQwIfcdsTERxBN/HjQ2kPr7zq
Tp+KqiVxjhF78oE0YnUObf4Z64y9BSuvRy/XuFy/8uCruUSi27sEXnSOUfJmDAif
LwIDAQAB""",
        'gateway': 'http://221.229.173.158:81/ccbypay.open/pdd/friendPay',
        'query_gateway': 'http://221.229.173.158:81/ccbypay.open/pdd/searchOrder',
    }
}


def _get_pri_key(mch_id):
    return APP_CONF[mch_id]['pri_key']


def _get_pub_key(mch_id):
    return APP_CONF[mch_id]['pub_key']


def _get_sys_pub_key(mch_id):
    return APP_CONF[mch_id]['sys_pub_key']


def _get_gateway(mch_id):
    return APP_CONF[mch_id]['gateway']


def _get_query_gateway(mch_id):
    return APP_CONF[mch_id]['query_gateway']


def _gen_rsa_sign(key, message):
    message = generate_sign(message)
    key_bytes = base64.b64decode(key)
    rsa_key = RSA.importKey(key_bytes)
    h = SHA256.new(message)
    signer = Signature_pkcs1_v1_5.new(rsa_key)
    signature = signer.sign(h)
    return base64.b64encode(signature)


def _verify_sign(data, signature, key):
    message = generate_sign(data)
    key_bytes = base64.b64decode(key)
    key = RSA.importKey(key_bytes)
    hash_value = SHA256.new(message)
    verifier = Signature_pkcs1_v1_5.new(key)
    if verifier.verify(hash_value, base64.b64decode(signature)):
        return True
    return False


def generate_sign(parameter):
    for k, v in parameter.items():
        if v == '':
            parameter.pop(k)
        parameter['charset'] = 'utf-8'
    par = sorted(parameter.items())
    param = ''
    for k, v in par:
        param += '%s=%s&' % (k, str(v))
    param = param[:-1]
    return param


# 创建支付订单
def create_charge(pay, pay_amount, info):
    app_id = info['app_id']
    service = info.get('service')
    pri_key = _get_pri_key(app_id)
    parameter_dict = OrderedDict((
        ('money', '%.2f' % pay_amount),
        ('outTradeNo', str(pay.id)),
        ('userAgent', service),
        ('appId', str(app_id)),
        ('notifyUrl', '{}/pay/api/{}/ccbypay/{}'.format(
            settings.NOTIFY_PREFIX, settings.NOTIFY_PATH, app_id)),
    ))
    parameter_dict['sign'] = _gen_rsa_sign(pri_key, parameter_dict)
    parameter_dict.pop('charset')
    _LOGGER.info('ccbypay create data:%s' % json.dumps(parameter_dict))
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    response = requests.post(_get_gateway(app_id), data=parameter_dict, headers=headers, timeout=5)
    data = json.loads(response.text)
    if data['code'] == '200':
        _LOGGER.info('ccbypay response url: %s', data['result']['url'])
        return {'charge_info': data['result']['url']}
    _LOGGER.info('ccbypay response msg: %s', data['message'])
    return {'charge_info': data['message']}


def verify_notify_sign(params, pub_key):
    sign = params.pop('sign')
    if not _verify_sign(params, sign, pub_key):
        raise ParamError('sign not pass, data: %s' % params)


# OK
def check_notify_sign(request, app_id):
    _LOGGER.info("ccbypay notify body: %s", request.body)
    data = dict(request.POST.items())
    _LOGGER.info("ccbypay notify data: %s, order_id is: %s", data, data['outTradeNo'])

    verify_notify_sign(data, _get_sys_pub_key(app_id))

    pay_id = data['outTradeNo']
    check_valid_ip_address(str(request.META['REMOTE_ADDR']), pay_id)
    if not pay_id:
        _LOGGER.error("ccbypay fatal error, out_trade_no not exists, data: %s", data)
        raise ParamError('ccbypay event does not contain pay ID')

    pay = order_db.get_pay(int(pay_id))
    if not pay:
        raise ParamError('ccbypay pay_id: %s invalid' % pay_id)
    if pay.status != PAY_STATUS.READY:
        raise ParamError('ccbypay pay %s has been processed' % pay_id)

    mch_id = pay.mch_id  # 商户编号
    trade_status = data['status']
    trade_no = data['outTradeNo']
    total_fee = float(data['money'])
    extend = {
        'trade_status': trade_status,
        'trade_no': trade_no,
        'total_fee': total_fee,
    }

    if trade_status == '2':
        check_channel_order(pay_id, total_fee, app_id)
        _LOGGER.info('ccbypay check order success, mch_id:%s pay_id:%s' % (mch_id, pay_id))
        order_db.add_pay_success(mch_id, pay_id, total_fee, trade_no, extend)
        # async notify
        async_job.notify_mch(pay_id)


def query_charge(pay_order, app_id):
    """ 查询订单 """
    pay_id = pay_order.id
    pri_key = _get_pri_key(app_id)
    parameter_dict = OrderedDict((
        ('outTradeNo', str(pay_id)),
        ('appId', app_id),
    ))
    parameter_dict['sign'] = _gen_rsa_sign(pri_key, parameter_dict)
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    response = requests.post(_get_query_gateway(app_id), data=parameter_dict, headers=headers, timeout=3)
    data = json.loads(response.text)
    if data['code'] == '200':
        _LOGGER.info('ccbypay query rsp: %s', data)
        trade_status = data['result']['status']
        total_fee = float(data['result']['money'])
        trade_no = data['result']['outTradeNo']
        extend = {
            'trade_status': trade_status,
            'trade_no': trade_no,
            'total_fee': total_fee,
        }
        if trade_status == '2':
            check_channel_order(pay_id, total_fee, app_id)
            _LOGGER.info('ccbypay query order success, mch_id:%s pay_id:%s' % (pay_order.mch_id, trade_no))
            order_db.add_pay_success(pay_order.mch_id, pay_order.id, total_fee, trade_no, extend)
            async_job.notify_mch(pay_order.id)
        else:
            _LOGGER.info('ccbypay query order success, but not pay success, pay pay_id: %s,trade_status:%s'
                         % (trade_no, trade_status))
    else:
        _LOGGER.warn('ccbypay query order error, status_code: %s', data['code'])
